import GameConfig from "../constant/GameConfig";
import { EPokerState } from "../constant/GameEnum";
import GameUtils from "../utils/GameUtils";
import ObjectUtils from "../utils/ObjectUtils";
import ClosePokerGroupVo from "./ClosePokerGroupVo";
import GameCtrl from "./GameCtrl";
import OpenPokerGroupVo from "./OpenPokerGroupVo";
import PlayPokerGroupVo from "./PlayPokerGroupVo";
import PokerGroupVo from "./PokerGroupVo";
import PokerVo from "./PokerVo";
import ReceivePokerGroupVo from "./ReceivePokerGroupVo";

export class AiPoker {
  private _pokers: PokerVo[] = [];
  public canOver: boolean = false;
  constructor() {
    this._pokers = [];
    this.canOver = false;
  }
  public addPoker(poker: PokerVo): void {
    this._pokers.push(poker);
  }
  public get pokers(): PokerVo[] {
    return this._pokers;
  }
}
/**
 * 模拟数据
 */
export default class ModelAI {
  public sortPokers: PokerVo[];
  constructor() {
    this.init();
  }
  /**
   * 初始化本轮扑克数据
   *
   */
  private init(): void {
    this.sortPokers = [];
    this._pokers = [];
    this._receivePokerGroup = [];
    this._playPokerGroup = [];
    this._closePokerArea.cleanPoker();
    this._openPokerArea.cleanPoker();
    this.creatrPoker();
    this.creatrReceivePokerGroup();
    this.creatrPlayPokerGroup();
    this.onPlay();
  }
  /**
   * 初始化一副扑克牌数据
   */
  private creatrPoker(): void {
    for (let i = 1; i <= 13; i++) {
      for (let j = 0; j < 4; j++) {
        let poker: PokerVo = new PokerVo(i, j, EPokerState.CLOSE);
        this.addPoker(poker);
      }
    }
  }
  /**
   * 初始化收牌区组数据
   */
  private creatrReceivePokerGroup(): void {
    for (let i = 0; i < GameConfig.CONST_RECEIVE_POKER_NUMBER; i++) {
      let receivePokerGroupVo: ReceivePokerGroupVo = new ReceivePokerGroupVo();
      receivePokerGroupVo.groupIndex = i;
      this.addReceivePokerVo(receivePokerGroupVo);
    }
  }
  /**
   * 初始化玩牌区组数据
   */
  private creatrPlayPokerGroup(): void {
    for (let i = 0; i < GameConfig.CONST_PLAY_POKER_NUMBER; i++) {
      let playPokerGroupVo: PlayPokerGroupVo = new PlayPokerGroupVo();
      playPokerGroupVo.groupIndex = i;
      this.addPlayPokerToGroup(playPokerGroupVo);
    }
  }
  /**
   * 开始游戏
   */
  private onPlay(): void {
    GameUtils.randomSort(this._pokers);

    let aiPoker = new AiPoker();
    this._pokers.map((poker) => {
      aiPoker.addPoker(poker);
    });
    GameCtrl.ins.addCanGameOverPoker(aiPoker);

    this._pokers.map((item) => {
      this._closePokerArea.addPoker(item);
    });

    for (let card = GameConfig.CONST_PLAY_POKER_NUMBER; card >= 0; card--) {
      for (let j = 0; j < card; j++) {
        let cardGroupIndex: number = GameConfig.CONST_PLAY_POKER_NUMBER - card + j;
        let cardGroup: PokerGroupVo = this._playPokerGroup[cardGroupIndex];
        let pokerVo: PokerVo = this._closePokerArea.topPoker;
        this._closePokerArea.delTopPoker();
        pokerVo.state = j === 0 ? EPokerState.OPEN : EPokerState.CLOSE;
        cardGroup.addPoker(pokerVo);
      }
    }
  }
  public AIplay() {
    if (this.checkCanAutoPlay()) {
      this.AIplay();
    } else {
      let aiPoker = GameCtrl.ins.getCanGameOverPoker(GameCtrl.ins.clearancePokers.length - 1);
      if (this.checkIsGameOver()) {
        aiPoker.canOver = true;
      } else {
        aiPoker.canOver = false;
      }
    }
  }
  /** 游戏是否结束*/
  private checkIsGameOver(): boolean {
    return this.getPokerNumInReceiveGroups() == GameConfig.POKER_NUM;
  }
  /**获取在receive区域的poker数量 */
  private getPokerNumInReceiveGroups(): number {
    let pokerNum: number = 0;
    for (let i = 0; i < this._receivePokerGroup.length; i++) {
      pokerNum += this._receivePokerGroup[i].Length;
    }
    return pokerNum;
  }
  /**
   * 1 优先翻转牌
   * @returns
   */
  private checkCanAutoPlay(): boolean {
    return (
      this.flipPlayToPlay() ||
      this.flipPlayToReceive() ||
      this.flipOpenToPlay$PlayTo() ||
      this.flipReceiveToPlay$PlayTo() ||
      this.flipCloseToPlay$PlayTo() ||
      this.flipUnderOpenToPlay$PlayTo() ||
      this.noFlipPlayToReceiveToCreateEmptyGroup() ||
      this.noFlipPlayToPlayToEmptyPlayGroup() ||
      this.noFlipPlayToPlay() ||
      this.noFlipOpenToPlay() ||
      this.noFlipCloseToPlay() ||
      this.noFlipUnderOpenToPlay() ||
      this.noFlipPlayToReceive() ||
      this.noFilpOpenToReceive() ||
      this.noFlipCloseToReceive() ||
      this.noFlipUnderOpenToReceive()
    );
  }

  /**
   * play to play 导致翻牌
   */
  private flipPlayToPlay(): boolean {
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];

      for (let fromIndex = 0; fromIndex < this._playPokerGroup.length; fromIndex++) {
        if (fromIndex == playIndex) {
          continue;
        }
        let FG = this._playPokerGroup[fromIndex];
        let rootOpenPoker = FG.rootOpenPoker;
        if (rootOpenPoker && playG.isNextPokerByTopPoker(rootOpenPoker)) {
          if (FG.topPoker === rootOpenPoker) {
            this.canMoveToPlay(rootOpenPoker);
          } else {
            this.moveFromPlayToPlayForGroup(rootOpenPoker);
          }
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  play to receive 导致翻牌
   */
  private flipPlayToReceive(): boolean {
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];
      for (let fromIndex = 0; fromIndex < this._receivePokerGroup.length; fromIndex++) {
        let RG = this._receivePokerGroup[fromIndex];
        let rootOpenPoker = playG.topPoker;
        if (playG.Length > 1) {
          let playGLastPokerState: boolean = playG.getPokerByIndex(playG.Length - 2).state == EPokerState.CLOSE;
          if (rootOpenPoker && RG.isNextPoker(rootOpenPoker) && playGLastPokerState) {
            this.canMoveToReceive(rootOpenPoker);
            return true;
          }
        }
      }
    }
    return false;
  }

  /**
   *  open to play $ play区域的其他牌可以移动到这组并翻牌
   * @returns
   */
  private flipOpenToPlay$PlayTo(): boolean {
    let topPoker = this._openPokerArea.topPoker;
    if (!topPoker) {
      return false;
    }
    let playIndex = this.getAutoPlayGroupIndex(topPoker);
    if (playIndex !== -1) {
      for (let index = 0; index < this._playPokerGroup.length; index++) {
        let PG = this._playPokerGroup[index];
        let fromPoker = PG.rootOpenPoker;
        if (fromPoker && this.isPlayNextPoker(fromPoker, topPoker)) {
          this.canMoveToPlay(topPoker);
          return true;
        }
      }
    }
    return false;
  }

  /**
   *
   * @returns receive to paly $ play区域的其他牌可以移动到这组并翻牌
   */
  private flipReceiveToPlay$PlayTo(): boolean {
    //
    for (let receiveIndex = 0; receiveIndex < this._receivePokerGroup.length; receiveIndex++) {
      let topPoker = this._receivePokerGroup[receiveIndex].topPoker;
      let playIndex = this.getAutoPlayGroupIndex(topPoker);
      if (playIndex !== -1) {
        for (let index = 0; index < this._playPokerGroup.length; index++) {
          let PG = this._playPokerGroup[index];
          let fromPoker = PG.rootOpenPoker;
          if (fromPoker && this.isPlayNextPoker(fromPoker, topPoker)) {
            this.canMoveToPlay(topPoker);
            return true;
          }
        }
      }
    }
    return false;
  }

  /**
   *  close to play $ play区域的其他牌可以移动到这组并翻牌
   * @returns
   */
  private flipCloseToPlay$PlayTo(): boolean {
    //
    let pokers = this._closePokerArea.pokers;
    for (let closeIndex = pokers.length - 1; closeIndex >= 0; closeIndex--) {
      //
      let playIndex = this.getAutoPlayGroupIndex(pokers[closeIndex]);
      if (playIndex !== -1) {
        this.moveFromCloseToOpen(this._closePokerArea.topPoker);
        return true;
      }
    }
    return;
  }

  /**
   * underopen to play $ play区域的其他牌可以移动到这组并翻牌
   * @returns
   */
  private flipUnderOpenToPlay$PlayTo(): boolean {
    let pokers = this._openPokerArea.pokers;
    for (let openIndex = 0; openIndex < pokers.length; openIndex++) {
      //
      let playIndex = this.getAutoPlayGroupIndex(pokers[openIndex]);
      if (playIndex !== -1) {
        if (this._closePokerArea.havePoker) {
          this.moveFromCloseToOpen(this._closePokerArea.topPoker);
        } else {
          this.moveOpenPokersToClose();
        }
        return true;
      }
    }
    return false;
  }

  /**
   * play to play 腾出一列空列（前提没有空列）
   * @returns
   */
  private noFlipPlayToPlayToEmptyPlayGroup(): boolean {
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];
      if (!playG.havePoker) {
        return false;
      }
    }
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];
      let startPoker = playG.getPokerByIndex(0);
      for (let fromIndex = 0; fromIndex < this._playPokerGroup.length; fromIndex++) {
        if (playIndex == fromIndex) {
          continue;
        }
        let FG = this._playPokerGroup[fromIndex];
        if (FG.isNextPokerByTopPoker(startPoker) && startPoker.state != EPokerState.CLOSE) {
          if (playG.topPoker === startPoker) {
            this.canMoveToPlay(startPoker);
          } else {
            this.moveFromPlayToPlayForGroup(startPoker);
          }
          return true;
        }
      }
    }
    return false;
  }

  /**
   * play to play
   * 禁止空白组 反复横跳
   * @returns
   */
  private noFlipPlayToPlay(): boolean {
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];
      if (!playG.havePoker) {
        continue;
      }
      let rootOpenPoker = playG.rootNoFlipOpenPoker;
      if (rootOpenPoker.point === 13 && playG.getIndexByGroup(rootOpenPoker) == 0) {
        continue;
      }
      for (let fromIndex = 0; fromIndex < this._playPokerGroup.length; fromIndex++) {
        if (playIndex == fromIndex) {
          continue;
        }
        let FG = this._playPokerGroup[fromIndex];
        if (FG.isNextPokerByTopPoker(rootOpenPoker)) {
          if (playG.topPoker === rootOpenPoker) {
            this.canMoveToPlay(rootOpenPoker);
          } else {
            this.moveFromPlayToPlayForGroup(rootOpenPoker);
          }
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  paly to receive
   * @returns
   */
  private noFlipPlayToReceive(): boolean {
    for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
      let playG = this._playPokerGroup[playIndex];
      for (let fromIndex = 0; fromIndex < this._receivePokerGroup.length; fromIndex++) {
        let RG = this._receivePokerGroup[fromIndex];
        let rootOpenPoker = playG.topPoker;
        if (rootOpenPoker && RG.isNextPoker(rootOpenPoker)) {
          this.canMoveToReceive(rootOpenPoker);
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  close to receive
   * @returns
   */
  private noFlipCloseToReceive(): boolean {
    //
    let pokers = this._closePokerArea.pokers;
    for (let closeIndex = pokers.length - 1; closeIndex >= 0; closeIndex--) {
      for (let receiveIndex = 0; receiveIndex < this._receivePokerGroup.length; receiveIndex++) {
        if (this._receivePokerGroup[receiveIndex].isNextPoker(pokers[closeIndex])) {
          this.moveFromCloseToOpen(this._closePokerArea.topPoker);
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  UnderOpen to receive
   * @returns
   */
  private noFlipUnderOpenToReceive(): boolean {
    let pokers = this._openPokerArea.pokers;
    for (let openIndex = 0; openIndex < pokers.length; openIndex++) {
      for (let receiveIndex = 0; receiveIndex < this._receivePokerGroup.length; receiveIndex++) {
        if (this._receivePokerGroup[receiveIndex].isNextPoker(pokers[openIndex])) {
          if (this._closePokerArea.havePoker) {
            this.moveFromCloseToOpen(this._closePokerArea.topPoker);
          } else {
            this.moveOpenPokersToClose();
          }
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  open to play
   * @returns
   */
  private noFlipOpenToPlay(): boolean {
    let topPoker = this._openPokerArea.topPoker;
    if (!topPoker) {
      return false;
    }
    for (let index = 0; index < this._playPokerGroup.length; index++) {
      let PG = this._playPokerGroup[index];
      if (PG.isNextPokerByTopPoker(topPoker)) {
        this.canMoveToPlay(topPoker);
      }
    }
    return false;
  }

  /**
   *  close to play
   * @returns
   */
  private noFlipCloseToPlay(): boolean {
    let pokers = this._closePokerArea.pokers;
    for (let closeIndex = pokers.length - 1; closeIndex >= 0; closeIndex--) {
      for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
        if (this._playPokerGroup[playIndex].isNextPokerByTopPoker(pokers[closeIndex])) {
          this.moveFromCloseToOpen(this._closePokerArea.topPoker);
          return true;
        }
      }
    }
    return false;
  }

  /**
   *  UnderOpen to play
   * @returns
   */
  private noFlipUnderOpenToPlay(): boolean {
    let pokers = this._openPokerArea.pokers;
    for (let openIndex = 0; openIndex < pokers.length; openIndex++) {
      for (let playIndex = 0; playIndex < this._playPokerGroup.length; playIndex++) {
        if (this._playPokerGroup[playIndex].isNextPokerByTopPoker(pokers[openIndex])) {
          if (this._closePokerArea.havePoker) {
            this.moveFromCloseToOpen(this._closePokerArea.topPoker);
          } else {
            this.moveOpenPokersToClose();
          }
          return true;
        }
      }
    }
    return false;
  }

  /**
   * play to receive 导致空列
   */
  private noFlipPlayToReceiveToCreateEmptyGroup(): boolean {
    for (let receiveIndex = 0; receiveIndex < this._receivePokerGroup.length; receiveIndex++) {
      let RG = this._receivePokerGroup[receiveIndex];
      for (let toIndex = 0; toIndex < this._playPokerGroup.length; toIndex++) {
        let topPoker = this._playPokerGroup[toIndex].topPoker;
        if (topPoker && RG.isNextPoker(topPoker) && this._playPokerGroup[toIndex].Length === 1) {
          this.canMoveToReceive(topPoker);
          return true;
        }
      }
    }
    return false;
  }

  /**
   * auto open to reveice
   * @returns
   */
  private noFilpOpenToReceive(): boolean {
    let topPoker = this._openPokerArea.topPoker;
    if (!topPoker) {
      return false;
    }
    let receiveIndex: number = this.getAutoReceiveGroupIndex(this.openPokerArea.topPoker);
    if (receiveIndex !== -1) {
      this.canMoveToReceive(topPoker);
      return true;
    }
    return false;
  }

  private getAutoPlayGroupIndex(poker: PokerVo): number {
    if (!poker) {
      return -1;
    }
    for (let index = 0; index < this._playPokerGroup.length; index++) {
      let PG = this._playPokerGroup[index];
      if (!PG.havePoker) {
        continue;
      }
      if (PG.isNextPokerByTopPoker(poker) && ((PG.topPoker && PG.topPoker.state != EPokerState.OPEN) || !PG.topPoker)) {
        return index;
      }
    }
    return -1;
  }

  private getAutoReceiveGroupIndex(poker: PokerVo): number {
    if (!poker) {
      return -1;
    }
    for (let i = 0; i < this._receivePokerGroup.length; i++) {
      let RG = this._receivePokerGroup[i];
      if (RG.isNextPoker(poker)) {
        return i;
      }
    }
    return -1;
  }

  /**
   * 查询play区域两个poker是否可以接续
   * @param fromPoker
   * @param toPoker
   * @returns
   */
  private isPlayNextPoker(fromPoker: PokerVo, toPoker: PokerVo): boolean {
    if (toPoker.point == fromPoker.point + 1 && (toPoker.suit + fromPoker.suit) % 2 != 0) {
      return true;
    }
    return false;
  }

  /**********    API   ***********/
  // 是否可以向play区域移动
  private canMoveToPlay(poker: PokerVo): boolean {
    let PPG: PlayPokerGroupVo[] = this._playPokerGroup;
    for (let i = 0; i < PPG.length; i++) {
      let playPG: PlayPokerGroupVo = PPG[i];
      if (playPG.isNextPokerByTopPoker(poker)) {
        poker.parent.delTopPoker();
        playPG.addPoker(poker);
        return true;
      }
    }
    return false;
  }

  // 是否可以向receive区域移动
  private canMoveToReceive(poker: PokerVo): boolean {
    for (let i = 0; i < this._receivePokerGroup.length; i++) {
      let receivePG: ReceivePokerGroupVo = this._receivePokerGroup[i];
      if (receivePG.isNextPoker(poker)) {
        poker.parent.delTopPoker();
        receivePG.addPoker(poker);
        return true;
      }
    }
    return false;
  }
  /**
   *连续的poker to play
   */
  private moveFromPlayToPlayForGroup(poker: PokerVo): void {
    let playGroupVo: PlayPokerGroupVo = poker.parent as PlayPokerGroupVo;
    let pokers: PokerVo[] = playGroupVo.getConnectPoker(poker);
    if (pokers.length > 1) {
      let PGP: PlayPokerGroupVo[] = this._playPokerGroup;
      for (let i = 0; i < PGP.length; i++) {
        let group: PlayPokerGroupVo = PGP[i];
        let groupIndex: number = (poker.parent as PlayPokerGroupVo).groupIndex;
        if (group.isNextPokerByTopPoker(pokers[0])) {
          let lastGroup: PlayPokerGroupVo = poker.parent as PlayPokerGroupVo;
          lastGroup.delTopPoker(pokers.length);
          for (let j = 0; j < pokers.length; j++) {
            group.addPoker(pokers[j]);
          }

          return;
        }
      }
    }
  }
  // close to open
  private moveFromCloseToOpen(poker: PokerVo) {
    poker.state = EPokerState.OPEN;
    this._closePokerArea.delTopPoker();
    this._openPokerArea.addPoker(poker);
  }

  //open to close
  private moveOpenPokersToClose(): void {
    let CP: ClosePokerGroupVo = this._closePokerArea;
    let OP: OpenPokerGroupVo = this._openPokerArea;
    if (!CP.havePoker) {
      for (let i = OP.pokers.length - 1; i >= 0; i--) {
        let poker: PokerVo = OP.topPoker;
        OP.delTopPoker();
        poker.state = EPokerState.CLOSE;
        this._closePokerArea.addPoker(poker);
      }
    }
  }

  /************** getter & setter *****************/

  /**扑克牌数据 */
  private _pokers: PokerVo[] = [];
  public get pokers(): PokerVo[] {
    return this._pokers;
  }
  public addPoker(vo: PokerVo) {
    this._pokers.push(vo);
  }
  /**close区域数据 */
  private _closePokerArea: ClosePokerGroupVo = new ClosePokerGroupVo();
  public get closePokerArea(): ClosePokerGroupVo {
    return this._closePokerArea;
  }

  /**open区域数据 */
  private _openPokerArea: OpenPokerGroupVo = new OpenPokerGroupVo();
  public get openPokerArea(): OpenPokerGroupVo {
    return this._openPokerArea;
  }
  /**收牌区数据组 */
  private _receivePokerGroup: ReceivePokerGroupVo[] = [];
  public get receivePokerGroup(): ReceivePokerGroupVo[] {
    return this._receivePokerGroup;
  }

  public addReceivePokerVo(vo: ReceivePokerGroupVo) {
    return this._receivePokerGroup.push(vo);
  }
  /**玩牌区数据组 */
  private _playPokerGroup: PlayPokerGroupVo[] = [];
  public get playPokerGroup(): PlayPokerGroupVo[] {
    return this._playPokerGroup;
  }
  public addPlayPokerToGroup(vo: PlayPokerGroupVo) {
    return this._playPokerGroup.push(vo);
  }

  private static _ins: ModelAI;
  public static get ins(): ModelAI {
    return (ModelAI._ins = ModelAI._ins || new ModelAI());
  }
}
